﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace RattlerRace
{
    /// <summary>
    /// AUTHOR: TADEJ GRUBER
    /// 
    /// To be updated
    /// </summary>
    public class Snake : Block
    {
        #region Spremenljivke in Properties
        //spremenljivke za position, življenja in telesce kače in če je sovražna
        private bool _is_enemy;

        public bool Is_enemy
        {
            get { return _is_enemy; }
            set { _is_enemy = value; }
        }

        private Point _position;

        public Point Position
        {
            get { return _position; }
            set { _position = value; }
        }

        private int _lives;

        public int Lives
        {
            get { return _lives; }
            set { _lives = value; }
        }

        private List<BodyPart> _body;

        public List<BodyPart> Body
        {
            get { return _body; }
            set { _body = value; }
        }

        private Points _dosezene_tocke;

        public Points Dosezene_tocke
        {
            get { return _dosezene_tocke; }
            set { _dosezene_tocke = value; }
        }

        //spremenljivki (seznama) za dodajanje dela telesca kače, nikjer drugje se ne kličejo, zato pa nisem nastavljal property-jev
        private List<BodyPart> _dodaj_body_on_apple_eaten;
        private List<BodyPart> _dodaj_body_on_certain_level;
        #endregion

        #region Konstruktorji
        public Snake()
        {
            _position = new Point();
            _body = new List<BodyPart>();
            _is_enemy = false;

            _lives = 3;

            _dosezene_tocke = new Points();

            _position.X = 20;
            _position.Y = 39;

            _dodaj_body_on_apple_eaten = new List<BodyPart>();
            _dodaj_body_on_certain_level = new List<BodyPart>();

            for (int i = 0; i < 4; i++)
                _dodaj_body_on_apple_eaten.Add(new BodyPart());
            
            int temp = 0;
            for (int i = 0; i < 6; i++)
            {
                _dodaj_body_on_certain_level.Add(new BodyPart(0, new Point(20, 35 + temp)));
                temp++;
            }

            _body.AddRange(_dodaj_body_on_certain_level);

            /*_position.X = 20;
            _position.Y = 35;*/
        }

        private void startingPos(Block[,] gameGrid, ref int x, ref int y)
        {
            for (int i = 1; i < 40; i++)
            {
                for (int j = 6; j < 40; j++)
                {
                    if (gameGrid[i, j] is BlankBlock &&
                        gameGrid[i, j - 1] is BlankBlock &&
                        gameGrid[i, j - 2] is BlankBlock &&
                        gameGrid[i, j - 3] is BlankBlock &&
                        gameGrid[i, j - 4] is BlankBlock &&
                        gameGrid[i, j - 5] is BlankBlock)
                    {
                        x = i;
                        y = j;
                    }
                }
            }
        }

        public Snake(bool enemy, Block[,] gameGrid)//ker je klic tega konstruktorja samo nastavil _is_enemy na true/false, ne pa ostalih stvari, sem povezal konstruktorja ©Tomaž Avberšek
        {
            _position = new Point();
            _body = new List<BodyPart>();
            _is_enemy = enemy;

            _dosezene_tocke = new Points();

            int x = 0, y = 6, dir = 2;
            startingPos(gameGrid, ref x, ref y);
            _position.X = x;
            _position.Y = y;

            _dodaj_body_on_apple_eaten = new List<BodyPart>();
            _dodaj_body_on_certain_level = new List<BodyPart>();

            for (int i = 0; i < 4; i++)
                _dodaj_body_on_apple_eaten.Add(new BodyPart());

            int temp = 0;
            for (int i = 0; i < 6; i++)
            {
                _dodaj_body_on_certain_level.Add(new BodyPart(dir, new Point(x, y - temp)));
                temp++;
            }

            _body.AddRange(_dodaj_body_on_certain_level);
        }
        #endregion

        #region Metode
        /// <summary>
        /// Ko kača poje jabolko, se bo klicala ta metoda, da se poveča telo kače
        /// </summary>
        public void addBodyPartsOnAppleEaten()
        {
            int size = _body.Count;
            _dodaj_body_on_apple_eaten.Clear();
            int lastDir = _body[size - 1].Smer;
            Point last = _body[size - 1].P;
            switch(lastDir)
            {
                case 0:
                    for (int i = 0; i < 1; i++)
                        _dodaj_body_on_apple_eaten.Add(new BodyPart(lastDir, new Point(last.X, last.Y + i)));
                    break;
                case 1:
                    for (int i = 0; i < 1; i++)
                        _dodaj_body_on_apple_eaten.Add(new BodyPart(lastDir, new Point(last.X - i, last.Y)));
                    break;
                case 2:
                    for (int i = 0; i < 1; i++)
                        _dodaj_body_on_apple_eaten.Add(new BodyPart(lastDir, new Point(last.X, last.Y - i)));
                    break;
                case 3:
                    for (int i = 0; i < 1; i++)
                        _dodaj_body_on_apple_eaten.Add(new BodyPart(lastDir, new Point(last.X + i, last.Y)));
                    break;
            }
            
            _body.AddRange(_dodaj_body_on_apple_eaten);
            //for(int i = _body.Count - 1,j = 0; j < 5; j++, i--)
            //{
            //    _body[i].Smer = _body[size-1].Smer;
            //}
        }

        /// <summary>
        /// Glede na level (nivo) se nastavi dolžina kače (nekateri bi radi to delali s svojo kačo ja :P)
        /// </summary>
        /// <param name="level"></param>
        public void initBodyPartsForLevels(int level)
        {
            if (((level % 5) == 1) && (level > 4))
            {
                _body.AddRange(_dodaj_body_on_certain_level);
            }
        }

        /// <summary>
        /// Metoda, ki spremeni smer premikanja kače, glede na pritisnjene tipke (0 - gor, 1 - desno, 2 - dol, 3 - levo)
        /// </summary>
        /// <param name="smer"></param>
        public void changeDirection(int smer, ref int block)
        {
            if (block < _body.Count)
                _body[block].Smer = smer;
            else
                block = 0;
        }

        /// <summary>
        /// Dodana metoda, ki posodobi smer celemu telesu ©Tomaž Avberšek
        /// </summary>
        public void changeBodyDirection()
        {
            for (int i = _body.Count - 1; i > 1; i--)
            {
                _body[i].Smer = _body[i - 1].Smer;
            }
        }

        /*ko uporabnik pritisne smerne tipke se ustrezno spremeni smer*/

        /// <summary>
        /// Metoda, ki doda življenje vsakič, ko se končajo dosežene točke s 50 (tu še tudi moramo preverit, ker se lahko zgodi, da prideš s točkami na 151, 152 ali 153 in ne ravno na 150 in se vseeno doda živlenje)
        /// </summary>
        public void addLife()
        {

        }

        /*dodaj življenje ko se rezultat konča s 50*/

        /// <summary>
        /// 
        /// </summary>
        public bool takeLife()
        {
            _lives--;
            if (_lives == -1)
            {
                return true;
            }
            return false;
        }


        /// <summary>
        /// Metoda, ki vrne zavoje za celo kačo (na ta način dobimo seznam za vse zavoje kače, pri čemer glava nima zavoja, zato ima seznam 1 element manj, kot je kača dolga ©Tomaž Avberšek
        /// </summary>
        /// <returns></returns>
        public List<int> getCurves()
        {
            List<int> curves = new List<int>();
            for(int i=0; i<_body.Count-1; i++)
            {
                curves.Add(_body[i].vrniZavoj(_body[i + 1].Smer)); //ni najbolj smiselno, ampak dobro
            }
            return curves;
        }

        /// <summary>
        /// Dodana metoda za premikanje kače (potrebujemo za game_grid ©Tomaž Avberšek
        /// </summary>
        /// <param name="direction"></param>
        public void moveSnake(int direction)
        {
            _body[0].Smer = direction;
            for (int i = _body.Count - 1; i > 0; i--)
            {
                _body[i].P = _body[i - 1].P;
            }
            switch(direction)
            {
                case 0: //gor
                    _body[0].P = new Point(_body[0].P.X, _body[0].P.Y - 1);
                    break;
                case 1: //desno
                    _body[0].P = new Point(_body[0].P.X + 1, _body[0].P.Y);
                    break;
                case 2: //dol
                    _body[0].P = new Point(_body[0].P.X, _body[0].P.Y + 1);
                    break;
                case 3: //levo
                    _body[0].P = new Point(_body[0].P.X - 1, _body[0].P.Y);
                    break;
            }
            changeBodyDirection();
        }

        /// <summary>
        /// Metoda, ki preverja smer relativno na kačo. Vedno preveri naprej in desno (če je polje naprej zasedeno, vrne desno smer. Če je polje naprej IN desno zasedeno, vrne levo smer. Sicer vrne smer. ©Tomaž Avberšek
        /// </summary>
        /// <param name="gameGrid"></param>
        /// <returns></returns>
        private int checkBlockAhead(Block[,] gameGrid)
        {
            Point current = _body[0].P;
            int x = current.X;
            int y = current.Y;
            int smer = _body[0].Smer; //med 0 in 3
            if(smer % 2 == 0) //smer je gor ali dol (0 ali 2)
            {
                int factor = smer == 0 ? 1 : -1;
                if (!(gameGrid[x, y - factor] is BlankBlock) && !(gameGrid[x, y - factor] is Apple)) //če je karkoli drugega, kot BlankBlock
                {
                    if (!(gameGrid[x + factor, y] is BlankBlock) && !(gameGrid[x + factor, y] is Apple)) //pogledamo, če tudi desno ni blankBlock
                    {
                        return (smer + 3) % 4; //ker je smer lahko samo 0 ali 2, vrne to 3 (namesto gor gre levo) ali 1 (namesto dol gre spet svojo levo, absolutna desna)
                    }
                    return (smer + 1) % 4;
                }
            }
            else //smer desno ali levo (1 ali 3)
            {
                int factor = smer == 1 ? 1 : -1;
                if (!(gameGrid[x + factor, y] is BlankBlock) && !(gameGrid[x + factor, y] is Apple))
                {
                    if (!(gameGrid[x, y + factor] is BlankBlock) && !(gameGrid[x, y + factor] is Apple))
                    {
                        return (smer + 3) % 4;
                    }
                    return (smer + 1) % 4;
                }
            }
            return _body[0].Smer;
        }

        /// <summary>
        /// Funckija, ki kliče drugo funkcijo (ni posebnega namena ©Tomaž Avberšek
        /// </summary>
        /// <param name="gameGrid"></param>
        /// <returns></returns>
        public int AI(Block[,] gameGrid)
        {
            //"AI", ki nadzira kačo
            int thisSmer = this._body[0].Smer;
            int newDir = checkBlockAhead(gameGrid);
            return newDir;
        }
        /*vzemi življenje*/
        #endregion
    }
}
